/*
kernel_kobj /sys/kernel/ 目录下
firmware_kobj /sys/firmware/ 目录下
fs_kobj /sys/fs/ 目录下
NULL /sys/ 目录下

udevadm monitor --env 命令可以接收到内核发送的 uevent 事件
*/


// #define pr_fmt(fmt)			"hgc: " fmt
#define pr_fmt(fmt)			"hgc:%s-%d:" fmt,__func__,__LINE__

#include <linux/sysfs.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/slab.h>

static struct kobject *hgc_kobj;

static struct kobject hgc_son_kobj;

//目录中所有属性文件统一的 show 操作
static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
	struct kobj_attribute *kattr;
	ssize_t ret = -EIO;

    pr_err("\n");
    //获取要操作的对应的属性文件
	kattr = container_of(attr, struct kobj_attribute, attr);
	if (kattr->show)
        //调用对应属性文件的 show 函数
		ret = kattr->show(kobj, kattr, buf);
	return ret;
}

//目录中所有属性文件统一的 store 操作
static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
		     const char *buf, size_t count)
{
    struct kobj_attribute *kattr;
	ssize_t ret = -EIO;

    pr_err("\n");
    //获取要操作的对应的属性文件
	kattr = container_of(attr, struct kobj_attribute, attr);
	if (kattr->store)
        //调用对应属性文件的 store 函数
		ret = kattr->store(kobj, kattr, buf, count);
	return ret;
}

//定义目录中所有属性的统一的操作
static const struct sysfs_ops hgc_sysfs_ops = {
	.show			= kobj_attr_show,
	.store			= kobj_attr_store,
};

//单个属性文件的 show 函数
static ssize_t son_show(struct kobject *kobj, struct kobj_attribute *attr,
		char *buf)
{
    pr_err("%s:%d\n",__func__,__LINE__);
    return sprintf(buf, "%s:%d\n",__func__,__LINE__);
}

//单个属性文件的 store 函数
static ssize_t son_store(struct kobject *kobj, struct kobj_attribute *attr,
		const char *buf, size_t count)
{
	int ret = 0;
    sscanf(buf,"%d",&ret);
    pr_err("%s:%d get=%d\n",__func__,__LINE__,ret);

	return count;
}

static ssize_t dau_show(struct kobject *kobj, struct kobj_attribute *attr,
		char *buf)
{
    pr_err("\n");
    return sprintf(buf, "%s:%d\n",__func__,__LINE__);
}

static ssize_t dau_store(struct kobject *kobj, struct kobj_attribute *attr,
		const char *buf, size_t count)
{
	int ret = 0;
    sscanf(buf,"%d",&ret);
    pr_err("get=%d\n",ret);

	return count;
}

/*
对应一个属性文件，读写函数名为 _name##_show 和 _name##_store
{
	.attr	= { .name = __stringify(_name),
		    .mode = VERIFY_OCTAL_PERMISSIONS(_mode) },
	.show	= _name##_show,
	.store	= _name##_store,
}
*/
static struct kobj_attribute hgc_son_value =
		__ATTR_RW_MODE(son, 0600);

static struct kobj_attribute hgc_dau_value =
		__ATTR_RW_MODE(dau, 0600);

//属性数组，存着所有属性文件
static struct attribute *default_attrs[] = {
	&hgc_son_value.attr,
	&hgc_dau_value.attr,
	NULL
};
/*
创建 default_group 和 *default_groups
static const struct attribute_group _name##_group = {
	.attrs = _name##_attrs,
};
static const struct attribute_group *_name##_groups[] = {
	&_name##_group,
	NULL,
}
*/
ATTRIBUTE_GROUPS(default);
static void dynamic_kobj_release(struct kobject *kobj);
static const struct kobj_type hgc_son_ktype = {
	.sysfs_ops		= &hgc_sysfs_ops,//default_groups 中所有属性文件统一的操作函数
	.default_groups		= default_groups,//属性数组，每个属性都会在目录中创建文件
	.release		= dynamic_kobj_release,
};

//释放kobj内存
static void dynamic_kobj_release(struct kobject *kobj)
{
	pr_err("(%p): %s\n", kobj, __func__);
	// kfree(kobj);
}

static int sys_init(void)
{
    int err = 0;

    //给 hgc_son_kobj 添加 hgc_son_ktype 并加入 hgc_kobj 目录
    err = kobject_init_and_add(&hgc_son_kobj,&hgc_son_ktype,hgc_kobj,"hgc_son");
    if(err){
        pr_err("\n");
        goto err;
    }

    const char *new_name = "test";
    //修改名字
    err = kobject_rename(&hgc_son_kobj, new_name);
    if(err){
        pr_err("ret=%d\n",err);
        goto err;
    }

    //不能调用该函数释放静态定义的 kobject.因为在上面的release中想用kfree来释放内存,这将导致内核报错
    // kobject_put(&hgc_son_kobj);
    //可以用这个函数
    // kobject_del(&hgc_son_kobj);
    return err;
err:
    kobject_del(&hgc_son_kobj);
    return err;
}

static int sys_create(void)
{
    int ret = 0;

    //在 /sys/ 目录下创建 hgc 文件夹.kobj_type使用了 dynamic_kobj_ktype .它会调用属性的show和store函数
    hgc_kobj = kobject_create_and_add("hgc",NULL);
    if (!hgc_kobj){
        pr_err("ret=%d\n",ret);
		return -ENOMEM;
    }

    //为文件添加属性
    ret = sysfs_create_group(hgc_kobj, &default_group);


    //发送内核添加事件
	kobject_uevent(hgc_kobj, KOBJ_ADD);

    return ret;
}

static int __init hgc_sys_init(void)
{
    int ret = 0;

    pr_err("\n");


    ret = sys_create();
    //设置名字hgc0,如果 kobj 已经加入系统则不会显示出新名字
    ret = kobject_set_name(hgc_kobj, "hgc%d",ret);
    if(ret){
        pr_err("ret=%d\n",ret);
        goto err;
    }

    ret = sys_init();
    if(ret){
        goto err;
    }

	return ret;



// int ret;

// 	bL_switcher_kobj = kobject_create_and_add("bL_switcher", kernel_kobj);
// 	if (!bL_switcher_kobj)
// 		return -ENOMEM;
// 	ret = sysfs_create_group(bL_switcher_kobj, &bL_switcher_attr_group);
// 	if (ret)
// 		kobject_put(bL_switcher_kobj);
err:
    kobject_del(hgc_kobj);
	return -EFAULT;
}

static void __exit hgc_sys_exit(void)
{
    //给应用层发送内核移除事件
    kobject_uevent(hgc_kobj,KOBJ_REMOVE);
    //先删除子目录
    kobject_del(&hgc_son_kobj);
    //再通过 release 释放内存
    kobject_put(&hgc_son_kobj);

    //删除对应目录
    kobject_del(hgc_kobj);
    //再通过 release 释放内存
    kobject_put(hgc_kobj);

    pr_err("\n");

}


module_init(hgc_sys_init);
module_exit(hgc_sys_exit);
MODULE_LICENSE("GPL");
